package com.jxpanda.spring.module.auth.config;

import com.jxpanda.spring.module.auth.config.properties.TokenProperties;
import com.jxpanda.spring.module.auth.core.token.DefaultReactiveOAuth2TokenGenerator;
import com.jxpanda.spring.module.auth.core.token.ReactiveOAuth2TokenGenerator;
import com.jxpanda.spring.module.auth.core.token.ReactiveOAuth2TokenService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2Token;

import java.time.Instant;
import java.util.Set;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@AutoConfigureAfter(JwtConfigure.class)
public class TokenConfigure {

    private final TokenProperties tokenProperties;


    @Bean("accessTokenGenerator")
    @ConditionalOnMissingBean(name = "accessTokenGenerator")
    public DefaultReactiveOAuth2TokenGenerator<OAuth2AccessToken> accessTokenGenerator() {
        TokenProperties.AccessToken accessTokenConfig = tokenProperties.getAccess();
        return DefaultReactiveOAuth2TokenGenerator.<OAuth2AccessToken>builder()
                .expiresIn(tokenProperties.getAccess().getExpiresIn())
                .tokenBuilder((userDetailsAuthenticationToken, tokenValue) -> {
                    Instant issuedAt = Instant.now();
                    Instant expiresAt = issuedAt.plusSeconds(accessTokenConfig.getExpiresIn());
                    Set<String> scopes = userDetailsAuthenticationToken.getUserDetails().getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
                    return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, tokenValue, issuedAt, expiresAt, scopes);
                })
                .build();
    }

    @Bean("refreshTokenGenerator")
    @ConditionalOnMissingBean(name = "refreshTokenGenerator")
    @ConditionalOnProperty(prefix = "panda.spring.auth.token.refresh", name = "enable", havingValue = "true")
    public DefaultReactiveOAuth2TokenGenerator<OAuth2RefreshToken> refreshTokenGenerator() {
        TokenProperties.RefreshToken refreshTokenConfig = tokenProperties.getRefresh();
        return DefaultReactiveOAuth2TokenGenerator.<OAuth2RefreshToken>builder()
                .expiresIn(tokenProperties.getRefresh().getExpiresIn())
                .tokenBuilder((userDetailsAuthenticationToken, tokenValue) -> {
                    Instant issuedAt = Instant.now();
                    Instant expiresAt = issuedAt.plusSeconds(refreshTokenConfig.getExpiresIn());
                    return new OAuth2RefreshToken(tokenValue, issuedAt, expiresAt);
                })
                .build();
    }

    @Bean("reactiveOAuth2TokenService")
    @DependsOn("accessTokenGenerator")
    @ConditionalOnBean(name = "accessTokenGenerator")
    public ReactiveOAuth2TokenService reactiveOAuth2TokenService(
            @Qualifier("accessTokenGenerator")
            ReactiveOAuth2TokenGenerator<? extends OAuth2Token> accessTokenGenerator,
            @Autowired(required = false)
            @Qualifier("refreshTokenGenerator")
            ReactiveOAuth2TokenGenerator<? extends OAuth2Token> refreshTokenGenerator) {
        return ReactiveOAuth2TokenService.builder()
                .accessTokenGenerator(accessTokenGenerator)
                .refreshTokenGenerator(refreshTokenGenerator)
                .build();
    }

}
